package com.vedana.bankmobile.dao.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.KeyRange;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Transaction;
import com.vedana.bankmobile.dao.OperationDao;
import com.vedana.bankmobile.shared.Operation;

/**
 * GAE Datastore DAO class for operations.
 * 
 * @author Didier MOISE
 *
 */
public class OperationDatastoreDao implements OperationDao {
	private DatastoreService datastore;

	/**
	 * Constructor
	 * 
	 * @param datastore Datastore Service.
	 */
	public OperationDatastoreDao(DatastoreService datastore) {
		this.datastore = datastore;
	}

	@Override
	public void addOperation(Operation operation) {
		Transaction tx = datastore.beginTransaction();

		Entity entity;
		entity = operationToNewEntity(operation);
		datastore.put(tx, entity);
		tx.commit();
	}

	@Override
	public List<Operation> listOperations(long accountId) {
		ArrayList<Operation> result = new ArrayList<Operation>();
		
		Key accountKey = KeyFactory.createKey("Account", accountId);
		Query q = new Query("Operation");
		q.addFilter("account", Query.FilterOperator.EQUAL, accountKey);

		PreparedQuery pq = datastore.prepare(q);

		for (Entity entity : pq.asIterable()) {
			Operation operation = entityToOperation(entity);
			result.add(operation);
		}
		
		return result;
	}

	@Override
	public List<Operation> listOperationsRange(long accountId, int start, int length) {
		ArrayList<Operation> result = new ArrayList<Operation>();
		
		Key accountKey = KeyFactory.createKey("Account", accountId);
		Query q = new Query("Operation");
		q.addFilter("account", Query.FilterOperator.EQUAL, accountKey);

		// PreparedQuery contains the methods for fetching query results
		// from the datastore
		PreparedQuery pq = datastore.prepare(q);
		for (Entity entity : pq.asList(FetchOptions.Builder.withLimit(length).offset(start))) {
			Operation operation = entityToOperation(entity);
			result.add(operation);
		}
		
		return result;
	}

	private Entity operationToNewEntity(Operation operation) {
		// Get and verify account foreign key
		Key accountKey = KeyFactory.createKey("Account", operation.getAccountId());
		// datastore.get(accountKey);

		// Automatically allocate operation key
		KeyRange range=datastore.allocateIds(accountKey, "Operation", 1);
		Entity entity = new Entity(range.getStart());

		entity.setProperty("date", operation.getDate());
		entity.setProperty("label", operation.getLabel());
		entity.setProperty("value", operation.getValue());
		entity.setProperty("account", KeyFactory.createKey("Account", operation.getAccountId()));
		
		return entity;
	}

	private Operation entityToOperation(Entity entity) {
		Operation operation = new Operation();
		
		operation.setDate((Date)entity.getProperty("date"));
		operation.setLabel((String)entity.getProperty("label"));
		operation.setValue((Double)entity.getProperty("value"));
		Key accountKey = (Key)entity.getProperty("account");
		operation.setAccountId(accountKey.getId());

		return operation;
	}
}
